iT邦幫忙

2024 iThome 鐵人賽

DAY 9
0

今天我們要做多選元件checkbox
就先參考昨天radio的寫法

一樣在element下面增加base-checkbox

ng g component base-checkbox

然後在main的設定檔裡面增加一組範例如下

        {
            name: 'checkbox1',
            cname: '多選1',
            inputType: 'checkbox',
            placeholder: '',
            required: true,
            defaultValue: '1',
            options: [
                { label: '選項1', value: '1' },
                { label: '選項2', value: '2' },
                { label: '選項3', value: '3' },
            ]
        }

接下來一樣在field.component.html增加一組給checkbox的模板

  <div *ngSwitchCase="'checkbox'">
    <app-base-checkbox
      [fieldSetting]="fieldSetting"
      [fieldObj]="fieldObj"
    ></app-base-checkbox>
  </div>

然後先參考昨天的寫法寫個一樣的checkbox

// base-checkbox.component.html

<div>
  <label class="checkbox" *ngFor="let option of fieldSetting?.options">
    <input
      type="checkbox"
      [(ngModel)]="value"
      [name]="fieldSetting.name"
      value="{{ option.value }}"
      (change)="valueChange()"
    />
    <span> <b></b>{{ option.label }}</span>
  </label>
</div>

// base-checkbox.component.ts

    @Input() fieldSetting!: FieldSetting;
    @Input() fieldObj!: any;
    value!: string;

    constructor() { }

    ngOnInit(): void
    {
        this.value = this.fieldObj[this.fieldSetting.name];
    }

    valueChange()
    {
        this.fieldObj[this.fieldSetting.name] = this.value;
    }

寫到這邊會發現 首頁上出來的元件好像有點奇怪
點選以後發現是連動的
這是因為我們綁到同一個value
畢竟這邊checkbox會是多選
所以數值紀錄的部分要調整一下寫法
這樣我們把value調整成selectedValue
裡面紀錄我們checkbox有勾選的value

著手開始改動程式
這邊在html會移除的ngModel
ts的話原本的value改成以下

    checkedValue!: string[];
    options!: any[];

checkedValue是個陣列 但設定檔裡面的defaultValue是字串
不想為了特例去改動設定檔
這邊先暫定規則 用逗號去紀錄已選項目
所以會用split(',') 去把字串解析成陣列

    this.checkedValue = this.fieldObj[this.fieldSetting.name].split(',');

selectedValue要怎麼跟options做關聯呢
思考了一下
這邊其實可以在元件裡面拷貝一份options並且增加一個checked的欄位
checked內的數值可以用selectedValue去對比該項目的value

    this.options = this.fieldSetting.options?.map(opt => { 
        return { ...opt, // 沿用原本options裡面的label跟value
                checked: !!this.checkedValue.find(c => c === opt.value) } // 直接find處理 並用雙否定轉成boolean值
                } 
        ) || []

後面補上 空陣列是因為有可能options會是空 加上個預設值是空陣列的概念

valueChange的部分 這邊如果觸發變動
則回傳對應項目的option.value
這邊的想法是用option.value去找到options裡面的對應項目
然後把checked顛倒過來

        let thisOption = this.options.find(opt => opt.value == value)
        thisOption.checked = !thisOption.checked;

options變動完會順便把變動的值更新回checkedValue

        this.checkedValue = this.options
            .filter(opt => opt.checked) // 篩選有勾選的 即checked為true
            .map(opt => opt.value) // checkedValue只要記錄value

然後把checkedValue的狀態更新回fieldObj

this.fieldObj[this.fieldSetting.name] = this.checkedValue.join(',');

這樣變更完的base-checkbox.component會是下列這樣

// html
<div>
  <label class="checkbox" *ngFor="let option of options">
    <input
      type="checkbox"
      [checked]="option.checked"
      [name]="fieldSetting.name"
      value="{{ option.value }}"
      (change)="valueChange(option.value)"
    />
    <span> <b></b>{{ option.label }}</span>
  </label>
</div>

//ts 
    @Input() fieldSetting!: FieldSetting;
    @Input() fieldObj!: any;
    checkedValue!: string[];
    options!: any[];

    constructor() { }

    ngOnInit(): void
    {
        this.checkedValue = this.fieldObj[this.fieldSetting.name].split(',');
        this.options = this.fieldSetting.options?.map(opt => { return { ...opt, checked: !!this.checkedValue.find(c => c === opt.value) } }) || []
    }

    valueChange(value: string)
    {
        let thisOption = this.options.find(opt => opt.value == value);
        thisOption.checked = !thisOption.checked;

        this.checkedValue = this.options.filter(opt => opt.checked).map(opt => opt.value);
        this.fieldObj[this.fieldSetting.name] = this.checkedValue.join(',');
    }

以上就是今天的多選元件部分
用了滿多陣列處理的方法
以前看別人寫都覺得這在炫技吧
開始用了以後覺得真的很方便 回不去了
在說明的時候就多加了一些折行方便閱讀
如果有看不懂的的可以在留言討論

今日程式:day09


上一篇
第8天 單選元件radio
下一篇
第10天 改寫元件for FormControl
系列文
簡單的事 最困難-Angular動態Form元件30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言